local lib = {}
local U = require "packages/utility/utility"



Fk:addPoxiMethod{
    name = "mcdh_overt",
    card_filter = function(to_select, selected, data,extra_data)
        --不能明置已明置的牌
        local card = Fk:getCardById(to_select)
        return card:getMark("@@mcdh_overt_card") == 0 and #selected < extra_data[2] and (Exppattern:Parse(extra_data[4]):match(card))
    end,
    feasible = function(selected, data, extra_data)
        return #selected <= extra_data[2] and #selected >= extra_data[1]
    end,
    prompt = function (data, extra_data)
        return extra_data[3]
    end
}
--- 明置多张牌，可明置别人的
---@param room Room @ 房间
---@param player ServerPlayer @ 要询问的玩家
---@param target ServerPlayer @ 被选择的玩家
---@param minNum integer @ 最小选牌数
---@param maxNum integer @ 最大选牌数
---@param skillName string @ 技能名
---@param cancelable boolean @ 是否可以取消。
---@param prompt  @  烧条信息
---@param pattern @仅选择自己的牌生效
---@param skipPublic boolean @ 是否跳过明置
---@return integer[] @ 选择的牌的id列表
lib.askForPublicCard  = function(room,player,target,minNum,maxNum,skillName,cancelable,prompt,pattern,skipPublic)
    local cards = {}
    if target == player then
        local canPublic = table.filter(target:getCardIds("h"), function(id)
            local card = Fk:getCardById(id)
            return card:getMark("@@mcdh_overt_card") == 0 and (Exppattern:Parse(pattern):match(card))
        end)

        --如果是自己的不足全选与匹配pattern牌
        if minNum > #canPublic and not cancelable then
            if not skipPublic then
                for _, id in ipairs(canPublic) do
                    local card = Fk:getCardById(id)
                    room:setCardMark(card,"@@mcdh_overt_card" , 1)
                end
            end
            return canPublic
        else
            --让玩家自己选择
            cards =room:askForPoxi(player,"mcdh_overt",{{"$Hand",target:getCardIds("h")}},{minNum,maxNum,prompt,pattern},cancelable)
        end

    else

        local canPublic = table.filter(target:getCardIds("h"), function(id)
            local card = Fk:getCardById(id)
            return card:getMark("@@mcdh_overt_card") == 0
        end)

        if minNum > #canPublic and not cancelable then
            if not skipPublic then
                for _, id in ipairs(canPublic) do
                    local card = Fk:getCardById(id)
                    room:setCardMark(card,"@@mcdh_overt_card" , 1)
                end
            end
            return canPublic
        end

        local data = {
            to = target.id,
            min = minNum,
            max = maxNum,
            skillName = skillName,
            prompt = prompt,
        }
        local cards_data = {}
        local visible_data = {}
        table.insert(cards_data, {"$Hand", canPublic})
        for _, id in ipairs(canPublic) do
            if not player:cardVisible(id) then
                visible_data[tostring(id)] = false
            end
        end
        if next(visible_data) == nil then visible_data = nil end
        data.visible_data = visible_data
        cards = room:askForPoxi(player, "AskForCardsChosen", cards_data, data, cancelable)
    end


    --明置牌
    if not skipPublic then
        for _, id in ipairs(cards) do
            local card = Fk:getCardById(id)
            room:setCardMark(card,"@@mcdh_overt_card" , 1)
        end
    end


    return cards
end
---获取玩家手中的暗置牌
---@param player ServerPlayer @玩家
---@return integer[] @ 选择的id
lib.getCanPublicCards = function(player)
    local canPublic = table.filter(player:getCardIds("h"), function(id)
        local card = Fk:getCardById(id)
        return card:getMark("@@mcdh_overt_card") == 0
    end)

    return canPublic
end

---获取玩家手中的明置牌
---@param player ServerPlayer @玩家
---@return integer[] @ 选择的id
lib.getPublicCards = function(player)
    local canPublic = table.filter(player:getCardIds("h"), function(id)
        local card = Fk:getCardById(id)
        return card:getMark("@@mcdh_overt_card") ~= 0
    end)

    return canPublic
end
--- 判断使用/打出事件是否是在使用明置牌
---@param event GameEvent @要判断使用/打出事件
---@return boolean
lib.IsUsingPubliccard = function(event)
    local cards = Card:getIdList(event.data[1].card)
    if #cards == 0 then return false end
    local moveEvents = event:searchEvents(GameEvent.MoveCards, 1, function(e)
        return e.parent and e.parent.id == event.id
    end)
    if #moveEvents == 0 then return false end
    local subcheck = table.simpleClone(cards)
    for _, move in ipairs(moveEvents[1].data) do
        if (move.moveReason == fk.ReasonUse or move.moveReason == fk.ReasonResonpse) then
            for _, info in ipairs(move.moveInfo) do
                if table.removeOne(subcheck, info.cardId) and not (moveEvents[1].data.extra_data and  moveEvents[1].data.extra_data.overt_cards and table.contains(moveEvents[1].data.extra_data.overt_cards,info.cardId) )   then
                    return false
                end
            end
        end
    end
    return #subcheck == 0
end


Fk:loadTranslationTable {
    ["@@mcdh_overt_card"] = "明置",
}
--- 获取其他玩家是否在玩家的攻击范围内。
--- 节选自player.lua，但最后调用lib.mcdh_player_distanceTo，方便预测玩家距离变动后是否还在攻击范围内（给阿消用）
---@param player Player @ 玩家
---@param other Player @ 其他玩家
---@param fixLimit? integer @ 卡牌距离限制增加专用
---@param fixDistance? integer @ 玩家距离的修正值，预测用
lib.mcdh_player_inMyAttackRange = function(player, other, fixLimit, fixDistance)
    assert(other:isInstanceOf(Player))
    if player == other or (other and (other.dead or other:isRemoved())) or player:isRemoved() then
        return false
    end

    fixLimit = fixLimit or 0
    fixDistance = fixDistance or 0
    local status_skills = Fk:currentRoom().status_skills[AttackRangeSkill] or Util.DummyTable
    for _, skill in ipairs(status_skills) do
        if skill:withoutAttackRange(player, other) then
            return false
        end
    end
    for _, skill in ipairs(status_skills) do
        if skill:withinAttackRange(player, other) then
            return true
        end
    end

    local baseAttackRange = player:getAttackRange()
    return math.max(lib.mcdh_player_distanceTo(player,other) + fixDistance,1) <= (baseAttackRange + fixLimit)
end
--- 获取玩家与其他角色的理论距离。
--- 节选自player.lua，但最后不会取math.max(ret, 1)
--- 通过 二者位次+距离技能之和 与 两者间固定距离 进行对比，更大的为实际距离。
---@param player Player @ 玩家
---@param other Player  @ 其他玩家
---@param mode? string  @ 计算模式(left/right/both)
---@param ignore_dead? boolean  @ 是否忽略尸体
lib.mcdh_player_distanceTo = function (player, other, mode, ignore_dead)
    assert(other:isInstanceOf(Player))
    mode = mode or "both"
    if other == player then return 0 end
    if not ignore_dead and other.dead then
        return -1
    end
    if player:isRemoved() or other:isRemoved() then
        return -1
    end
    local right = 0
    local temp = player
    local try_time = 10
    for _ = 0, try_time do
        if temp == other then break end
        if (ignore_dead or not temp.dead) and not temp:isRemoved() then
            right = right + 1
        end
        temp = temp.next
    end
    if temp ~= other then
        print("Distance malfunction: start and end does not match.")
    end
    local left = #(ignore_dead and Fk:currentRoom().players or Fk:currentRoom().alive_players) - right - #table.filter(Fk:currentRoom().alive_players, function(p) return p:isRemoved() end)
    local ret = 0
    if mode == "left" then
        ret = left
    elseif mode == "right" then
        ret = right
    else
        ret = math.min(left, right)
    end

    local status_skills = Fk:currentRoom().status_skills[DistanceSkill] or Util.DummyTable
    for _, skill in ipairs(status_skills) do
        local fixed = skill:getFixed(player, other)
        local correct = skill:getCorrect(player, other)
        if fixed ~= nil then
            ret = fixed
            break
        end
        ret = ret + (correct or 0)
    end

    if player.fixedDistance[other] then
        ret = player.fixedDistance[other]
    end

    return ret
end


--- 给某个武将添加初始蓄力或弹药的描述（实际上是生成了个无触发事件的触发技）
--- @param general General
--- @param st integer
--- @param stmax integer
--- @param kind "charge"|"ammo"
lib.mcdh_chargeammoskill_general = function (general,st,stmax,kind)
    if kind == "charge" then
        general.maxCharge = stmax
        general.charge = st
    else
        general.maxAmmo = stmax
        general.ammo = st
    end
    local name = "#mcdh_general_"..general.name..kind..tostring(st)..tostring(stmax).."&"
    local exname = kind == "charge" and "初始技力" or "初始弹药"
    local detail = ":#mcdh_general_"..general.name..kind..tostring(st)..tostring(stmax).."&"
    local exdetail = tostring(st).."/"..tostring(stmax)
    local tem_skill = TriggerSkill:new(name, Skill.NotFrequent)
    general:addSkill(tem_skill)
    Fk:loadTranslationTable {
        [name] = exname,
        [detail] = exdetail,
    }
end

--- 更改某角色蓄力技的能量或能量上限
---@param player ServerPlayer @ 角色
---@param num? integer @ 改变的能量
---@param max? integer @ 改变的能量上限
lib.mcdh_charge = function(player, num, max)
    local room = player.room
    num = num or 0
    max = max or 0
    local max_num = math.max(0, player:getMark("mcdh_charge_max") + max)
    local new_num = math.min(max_num, math.max(0, player:getMark("mcdh_charge") + num))
    room:setPlayerMark(player, "mcdh_charge_max", max_num)
    room:setPlayerMark(player, "mcdh_charge", new_num)
    room:setPlayerMark(player, "@mcdh_charge", (max_num == 0 and num == 0) and 0 or (new_num .. "/" .. max_num))
end
lib.RecoverCharge = function(from,who,num,skillName)
    local room = who.room
    local logic = room.logic
    local data = {
        who = who,
        from = from,
        num = num,
        skillName = skillName,
    }
    if logic:trigger("fk.PreRecoverCharge", who, data) or data.num < 1 then
        return false
    end
    data.num = math.min(data.num, who:getMark("mcdh_charge_max")- who:getMark("mcdh_charge"))
    if num == 0 then return  false end
    lib.mcdh_charge(who, data.num)
    --回复技力后
    logic:trigger("fk.AfterRecoverCharge", who, data)
end
lib.LoseCharge = function(from,who,num,skillName)
    local room = who.room
    local logic = room.logic
    -- 失去非正数是非法的
    if num < 1 then
        return false
    end
    local data = {
        who = who,
        from = from,
        num = num,
        skillName = skillName,
    }
    if logic:trigger("fk.PreLoseCharge", who, data) or data.num < 1 then
        return false
    end
    data.num = math.min(data.num, who:getMark("mcdh_charge"))
    if num == 0 then return  false end
    lib.mcdh_charge(who, -data.num)
    --装填弹药后
    logic:trigger("fk.AfterLoseCharge", who, data)
end
Fk:loadTranslationTable {
    ["@mcdh_charge"] = "技力",
}
--- 更改某角色装填技技弹药或弹药上限
---@param player ServerPlayer @ 角色
---@param num? integer @ 改变的能量
---@param max? integer @ 改变的能量上限
lib.mcdh_ammo = function(player, num, max)
    local room = player.room
    num = num or 0
    max = max or 0
    local max_num = math.max(0, player:getMark("mcdh_ammo_max") + max)
    local new_num = math.min(max_num, math.max(0, player:getMark("mcdh_ammo") + num))
    room:setPlayerMark(player, "mcdh_ammo_max", max_num)
    room:setPlayerMark(player, "mcdh_ammo", new_num)
    room:setPlayerMark(player, "@mcdh_ammo", (max_num == 0 and num == 0) and 0 or (new_num .. "/" .. max_num))
end
lib.LoadAmmo =function(from,who,num,skillName)
    local room = who.room
    local logic = room.logic
    -- 装填非正数是非法的
    if num < 1 then
        return false
    end
    local data = {
        who = who,
        from = from,
        num = num,
        skillName = skillName,
    }
    if logic:trigger("fk.PreLoadAmmo", who, data) or data.num < 1 then
        return false
    end
    data.num = math.min(data.num,lib.mcdh_getLoseammo(who))
    if data.num == 0  then return false end
    --修改弹药
    lib.mcdh_ammo(who, data.num)
    room.logic:trigger("fk.AfterLoadAmmo", who, data)

end
lib.LoseAmmo = function(from,who,num,skillName)
    local room = who.room
    local logic = room.logic
    -- 失去非正数是非法的
    if num < 1 then
        return false
    end
    local data = {
        who = who,
        from = from,
        num = num,
        skillName = skillName,
    }
    if logic:trigger("fk.PreLoseAmmo", who, data) or data.num < 1 then
        return false
    end
    data.num = math.min(data.num, who:getMark("mcdh_ammo"))
    if num == 0 then return  false end
    lib.mcdh_ammo(who, -data.num)
    --装填弹药后
    logic:trigger("fk.AfterLoseAmmo", who, data)
end
lib.mcdh_getLoseammo = function(player)
    local room = player.room
    return player:getMark("mcdh_ammo_max") - player:getMark("mcdh_ammo")
end
Fk:loadTranslationTable {
    ["@mcdh_ammo"] = "弹药",
}

--- 获取某角色手中伤害牌
---@param player ServerPlayer @ 角色
lib.getDamageCard = function(player)
    local room = player.room
    local result = {}
    local  cards = player:getCardIds(Player.Hand)
    for _, id in ipairs(cards) do
        local  card = Fk:getCardById(id)
        if card.is_damage_card then
            table.insertIfNeed(result,id)
        end
    end
    return result
end
lib.suitToString = function(suit,symbol)
    local ret
    if suit == Card.Spade then
        ret = "spade"
    elseif suit == Card.Heart then
        ret = "heart"
    elseif suit == Card.Club then
        ret = "club"
    elseif suit == Card.Diamond then
        ret = "diamond"
    else
        ret = "nosuit"
    end
    return symbol and "log_" .. ret or ret
end
--获取一系列牌中花色最多的花色
lib.MaxsuitToString = function(cards,symbol)
    local result = {0,0,0,0,0}
    for _, id in ipairs(cards) do
        result[Fk:getCardById(id).suit] = result[Fk:getCardById(id).suit] + 1
    end
    local suit = {}
    local n = 0
    for  i=1 , 5 ,1 do
        if result[i] >n then
            suit = {}
            table.insert(suit, lib.suitToString(i,symbol))
            n = result[i]
        elseif result[i] == n and n ~= 0 then
            table.insert(suit, lib.suitToString(i,symbol))
        end
    end
    return suit
end
lib.SortCardbySuit = function(cards)
    local suit = {[1] ={},[2] = {} ,[3] = {},[4] = {}}
    for _,id in ipairs(cards) do
        local card = Fk:getCardById(id)
        table.insert(suit[card.suit],id)
    end
    local sort = {}
    for _,c in ipairs(suit) do
        table.insertTable(sort,c)
    end
    return sort
end

lib.SortPlayId = function(room,playerIds,first)
    table.sort(playerIds, function(prev, next)
        local prevSeat = room:getPlayerById(prev).seat
        local nextSeat = room:getPlayerById(next).seat

        return prevSeat < nextSeat
    end)
    if first and
            table.find( playerIds, function(id)
                return room:getPlayerById(id).seat >= first.seat
            end)
    then
        while room:getPlayerById(playerIds[1]).seat < first.seat do
            local toPlayerId = table.remove(playerIds, 1)
            table.insert(playerIds, toPlayerId)
        end
    end
    return playerIds
end
---造成一次伤害
---@param data DamageStruct
lib.Damage = function(data)
    if data.to then
        --如果伤害来源已经死亡则改为无来源伤害
        if data.from and data.from.dead then
            data.from = nil
        end
        --如果没有死亡就造成伤害
        if data.to and not data.from.dead then
            data.to.room:damage(data)
        end
    end
end

-- 为使用事件添加一个额外目标，多用于fk.AfterCardTargetDeclared，可适用借刀
---@param player ServerPlayer
---@param data CardUseStruct @ 使用事件的data
---@param bypass_distances boolean? @ 是否无距离关系的限制
---@param skillName string? @ 技能名，仅用于烧条提示
lib.askForAddTarget = function(player, data, bypass_distances, skillName)
    local room = player.room
    local extra = room:getUseExtraTargets(data, bypass_distances)
    if #extra == 0 then return {} end
    local prompt = "#mcdh_addtarget-choose::"..data.from..":"..data.card:toLogString()..":"..skillName
    local tos = room:askForChoosePlayers(player, extra, 1, 1, prompt, skillName, false)
    if #tos > 0 and data.card.skill:getMinTargetNum() > 1 and #data.tos > 0 and data.tos[1][2] then
        local subtarget = data.tos[1][2]
        table.insert(tos, subtarget)
    end
    return tos
end

Fk:loadTranslationTable{
    ["#mcdh_addtarget-choose"] = "%arg2：可以为 %dest 使用的%arg增加一个目标",
}





Fk:addTargetTip{
    name = "mcdh_distance_tip",
    target_tip = function(self,player, to_select, selected, selected_cards, card, selectable,extra_data)
        if not selectable then return end
        return "#mcdh_distance_tip:::"..player:distanceTo(to_select)
    end,
}
Fk:loadTranslationTable {
    ["#mcdh_distance_tip"] = "距离%arg"
}

--- AmmoAndChargeData 
---@class AmmoAndChargeDataSpec
---@field public from ServerPlayer @ 执行者
---@field public num   integer     @ 数量
---@field public type  string      @ 类型
---@field public skil_name string  @ 技能名
---@field public method    string  @ 操作
---@field public prevented boolean? @是否被防止

---@class lib.AmmoAndChargeData: AmmoAndChargeDataSpec, TriggerData
lib.Ammo_ChargeData = TriggerData:subclass("AmmoAndChargeData")

--- 弹药或技力改变 TriggerEvent
---@class lib.AmmoAndChargeTE: TriggerEvent
---@field public data lib.AmmoAndChargeData
lib.AmmoAndChargeTE = TriggerEvent:subclass("AmmoAndChargeEvent")

--- 技力或弹药改变前
---@class lib.PreAmmoAndCharge: lib.AmmoAndChargeTE
lib.PreAmmoAndCharge = lib.AmmoAndChargeTE:subclass("lib.PreAmmoAndCharge")
--- 技力或弹药改变后
---@class lib.AfterAmmoAndCharge: lib.AmmoAndChargeTE
lib.AfterAmmoAndCharge = lib.AmmoAndChargeTE:subclass("lib.AfterAmmoAndCharge")


---@alias AmmoAndChargeTrigFunc fun(self: TriggerSkill, event: lib.AmmoAndChargeTE,
---  target: ServerPlayer, player: ServerPlayer, data: lib.AmmoAndChargeData ):any

---@class SkillSkeleton
---@field public addEffect fun(self: SkillSkeleton, key: lib.AmmoAndChargeTE,
---  data: TrigSkelSpec<AmmoAndChargeTrigFunc>, attr: TrigSkelAttribute?): SkillSkeleton

--- 技力或弹药 GameEvent
lib.AmmoAndChargeEvent = "AmmoAndCharge"


function lib.PreAmmoAndCharge:breakCheck()
    return self.data.num < 0 or self.data.prevented
end

Fk:addGameEvent(lib.AmmoAndChargeEvent, nil, function (self)
  local AmmoAndChargeData = self.data ---@class AmmoAndChargeDataSpec
  local room = self.room ---@type Room
  local logic = room.logic
  local from = AmmoAndChargeData.from
   logic:trigger(lib.PreAmmoAndCharge, from, AmmoAndChargeData)
   if AmmoAndChargeData.prevented or AmmoAndChargeData.num < 0 then
    logic:breakEvent(false)
   end
   local num = AmmoAndChargeData.num
   if AmmoAndChargeData.method == "Lose" then
        num = -num
   end
   if AmmoAndChargeData.type == "ammo" then
    lib.mcdh_ammo(AmmoAndChargeData.from, num)
   elseif AmmoAndChargeData.type == "charge" then
    lib.mcdh_charge(AmmoAndChargeData.from,num)
   end
   logic:trigger(lib.AfterAmmoAndCharge, from, AmmoAndChargeData)
end)


---@return boolean
local function exec(tp, ...)
    local event = tp:create(...)
    local _, ret = event:exec()
    return ret
  end


--- 根据技力和弹药数据执行效果。
---@param ammo_chargeData AmmoAndChargeDataSpec
---@return AmmoAndChargeDataSpec
function lib.AmmoAndCharge(ammo_chargeData)
    local data = lib.Ammo_ChargeData:new(ammo_chargeData)
    local event = GameEvent[lib.AmmoAndChargeEvent]:create(data)
    local _, ret = event:exec()
    return data
end


---@return boolean
local function exec(tp, ...)
  local event = tp:create(...)
  local _, ret = event:exec()
  return ret
end

local function convertOldMoveInfo(info)
  local room = Fk:currentRoom()
  if type(info.from) == "number" then
    info.from = room:getPlayerById(info.from)
  end
  if type(info.to) == "number" then
    info.to = room:getPlayerById(info.to)
  end
  if type(info.proposer) == "number" then
    info.proposer = room:getPlayerById(info.proposer)
  end
  if info.visiblePlayers then
    if type(info.visiblePlayers) == "table" and #info.visiblePlayers > 0 and type(info.visiblePlayers[1]) == "number" then
      info.visiblePlayers = table.map(info.visiblePlayers, Util.Id2PlayerMapper)
    elseif type(info.visiblePlayers) == "number" then
      info.visiblePlayers = room:getPlayerById(info.visiblePlayers)
    end
  end
end

---@param info MoveCardsData | CardsMoveInfo
local function infoCheck(info)
  assert(table.contains({ Card.PlayerHand, Card.PlayerEquip, Card.PlayerJudge, Card.PlayerSpecial, Card.Processing, Card.DrawPile, Card.DiscardPile, Card.Void }, info.toArea))
  assert(info.toArea ~= Card.PlayerSpecial or type(info.specialName) == "string")
  assert(type(info.moveReason) == "number")
end


local function moveInfoTranslate(room, ...)
  ---@type MoveCardsData[]
  local ret = {}
  for _, cardsMoveInfo in ipairs{ ... } do
    convertOldMoveInfo(cardsMoveInfo)
    if #cardsMoveInfo.ids == 0 then goto continue end
    infoCheck(cardsMoveInfo)

    -- 若移动到被废除的装备栏，则修改，否则原样
    local infos = {} ---@type MoveInfo[]
    local abortMoveInfos = {} ---@type MoveInfo[]
    for _, id in ipairs(cardsMoveInfo.ids) do
      local toAbortDrop = false
      if cardsMoveInfo.toArea == Card.PlayerEquip and cardsMoveInfo.to then
        local moveToPlayer = cardsMoveInfo.to
        ---@cast moveToPlayer -nil 防止判空波浪线
        local card = moveToPlayer:getVirualEquip(id) or Fk:getCardById(id)
        if card.type == Card.TypeEquip and #moveToPlayer:getAvailableEquipSlots(card.sub_type) == 0 then
          table.insert(abortMoveInfos, {
            cardId = id,
            fromArea = room:getCardArea(id),
            fromSpecialName = cardsMoveInfo.from and cardsMoveInfo.from:getPileNameOfId(id),
          })
          toAbortDrop = true
        end
      end

      if not toAbortDrop then
        table.insert(infos, {
          cardId = id,
          fromArea = room:getCardArea(id),
          fromSpecialName = cardsMoveInfo.from and cardsMoveInfo.from:getPileNameOfId(id),
        })
      end
    end

    -- 为普通移动设置正确数据
    if #infos > 0 then
      table.insert(ret, MoveCardsData:new {
        moveInfo = infos,
        from = cardsMoveInfo.from,
        to = cardsMoveInfo.to,
        toArea = cardsMoveInfo.toArea,
        moveReason = cardsMoveInfo.moveReason,
        proposer = cardsMoveInfo.proposer,
        skillName = cardsMoveInfo.skillName,
        moveVisible = cardsMoveInfo.moveVisible,
        specialName = cardsMoveInfo.specialName,
        specialVisible = cardsMoveInfo.specialVisible,
        drawPilePosition = cardsMoveInfo.drawPilePosition,
        moveMark = cardsMoveInfo.moveMark,
        visiblePlayers = cardsMoveInfo.visiblePlayers,
        extra_data = cardsMoveInfo.extra_data,
      })
    end

    -- 为因为移动到废除区域而改为丢掉的设置正确数据
    if #abortMoveInfos > 0 then
      table.insert(ret, MoveCardsData:new {
        moveInfo = abortMoveInfos,
        from = cardsMoveInfo.from,
        toArea = Card.DiscardPile,
        moveReason = fk.ReasonPutIntoDiscardPile,
        moveVisible = true,
      })
    end
    ::continue::
  end
  return ret
end

function lib.moveCardsWithExtraData(room,extra_data,...)
     local datas = moveInfoTranslate(room, ...)
  if #datas == 0 then
    return false
  end
  for _, data in pairs(datas) do
    data.extra_data = extra_data
  end
  return exec(GameEvent.MoveCards,datas)
end

return lib
